package CoreNLPummarizer

import (
	"fmt"
	"regexp"
	"strings"
	"sync"
	"unicode/utf8"

	"github.com/DavidBelicza/TextRank/v2/textrank" // TextRank算法实现 [1, 2]
	"github.com/go-ego/gse"                        // 中文分词库 [3, 4, 5]
	"github.com/pemistahl/lingua-go"               // 语言检测库 [6, 7]
	"github.com/sentencizer/sentencizer"           // 英文句子分割库 [8]
	"github.com/writeas/go-strip-markdown"         // Markdown标签剥离库 [9, 10]
)

// Language 表示文本的语言。
type Language string

const (
	English Language = "en"   // 英文
	Chinese Language = "zh"   // 中文
	Auto    Language = "auto" // 自动检测语言
)

// SummarizeOptions 允许自定义摘要过程。
type SummarizeOptions struct {
	TargetLength int      // 摘要的目标字符长度 (rune count)
	Language     Language // 指定语言："en", "zh", 或 "auto"
	ChunkSize    int      // 递归摘要时每个分块的最大字符数。默认值：5000。
	MaxRecursion int      // 递归摘要的最大递归深度。默认值：3。
}

// defaultSummarizeOptions 提供摘要的默认参数。
func defaultSummarizeOptions() SummarizeOptions {
	return SummarizeOptions{
		TargetLength: 200,  // 默认目标长度为200字符
		Language:     Auto, // 默认自动检测语言
		ChunkSize:    5000, // 默认分块大小为5000字符
		MaxRecursion: 3,    // 默认最大递归深度为3
	}
}

// Summarize 接收内容和选项，返回一个摘要字符串。
// 这是模块的统一入口方法。
func Summarize(content string, opts SummarizeOptions) (string, error) {
	// 应用默认选项，如果用户未提供
	if opts.TargetLength == 0 {
		opts.TargetLength = defaultSummarizeOptions().TargetLength
	}
	if opts.Language == "" {
		opts.Language = defaultSummarizeOptions().Language
	}
	if opts.ChunkSize == 0 {
		opts.ChunkSize = defaultSummarizeOptions().ChunkSize
	}
	if opts.MaxRecursion == 0 {
		opts.MaxRecursion = defaultSummarizeOptions().MaxRecursion
	}

	// 1. 输入预处理：剥离HTML/Markdown标签并规范化空白符。
	cleanedContent := preprocessText(content)
	if cleanedContent == "" {
		return "", nil // 清理后内容为空则返回空字符串
	}

	// 2. 语言检测：识别清理后文本的语言。
	detectedLang, err := detectLanguage(cleanedContent, opts.Language)
	if err != nil {
		return "", fmt.Errorf("语言检测失败: %w", err)
	}

	// 3. 文本分割：将文本分解为独立的句子。
	sentences, err := segmentText(cleanedContent, detectedLang)
	if err != nil {
		return "", fmt.Errorf("文本分割失败: %w", err)
	}
	if len(sentences) == 0 {
		return "", nil // 分割后未找到句子
	}

	// 4. 递归摘要：处理超长文档，通过分块和迭代细化来管理内存和性能。
	finalSummarySentences, err := recursiveSummarize(sentences, detectedLang, opts.ChunkSize, opts.MaxRecursion)
	if err != nil {
		return "", fmt.Errorf("递归摘要失败: %w", err)
	}
	if len(finalSummarySentences) == 0 {
		return "", nil // 未生成摘要句子
	}

	// 5. 输出生成：组合选定的句子并截断到目标字符长度。
	finalSummary := generateFinalSummary(finalSummarySentences, opts.TargetLength)

	return finalSummary, nil
}

// preprocessText 处理HTML/Markdown标签剥离和通用文本规范化。
// 它使用简单的正则表达式移除HTML标签，并使用go-strip-markdown移除Markdown格式。
// 同时规范化所有空白符。
func preprocessText(text string) string {
	// 首先剥离Markdown格式 [9, 10]
	text = stripmd.Strip(text)

	// 使用简单的正则表达式剥离HTML标签 [11, 12]
	// 此正则表达式旨在进行简单的标签移除，而非完整的HTML解析。
	htmlRegex := regexp.MustCompile(`(?i)<[^>]*>`) // 不区分大小写，匹配任何HTML标签
	text = htmlRegex.ReplaceAllString(text, "")

	// 规范化空白符：将多个空格、制表符、换行符替换为单个空格
	spaceRegex := regexp.MustCompile(`\s+`)
	text = spaceRegex.ReplaceAllString(text, " ")

	// 移除文本开头和结尾的空白符
	text = strings.TrimSpace(text)

	return text
}

// detectLanguage 识别输入文本的语言。
// 它使用lingua-go库进行准确的语言检测。
// 如果preferredLang不是Auto，则直接使用指定的语言。
func detectLanguage(text string, preferredLang Language) (Language, error) {
	if preferredLang != Auto {
		return preferredLang, nil
	}

	// 初始化lingua-go检测器 [6, 7]
	// 使用FromAllLanguages进行广泛检测，也可以优化为FromLanguages(lingua.English, lingua.Chinese)
	detector := lingua.NewLanguageDetectorBuilder().
		FromAllLanguages().
		WithPreloadedLanguageModels(). // 预加载模型以提高重复调用的性能
		Build()

	language, _ := detector.DetectLanguageOf(text) // 执行语言检测
	switch language {
	case lingua.English:
		return English, nil
	case lingua.Chinese:
		return Chinese, nil
	default:
		// 回退方案：如果检测不确定或不支持的语言，默认使用英文。
		// 这是一个实用的选择，以增强健壮性。
		return English, nil
	}
}

// segmentText 根据检测到的语言将清理后的文本分解为独立的句子。
// 英文使用sentencizer，中文使用gse和自定义逻辑。
func segmentText(text string, lang Language) (string, error) {
	var sentences []string
	switch lang {
	case English:
		segmenter := sentencizer.NewSegmenter("en") // 初始化英文句子分割器 [8]
		sentences = segmenter.Segment(text)         // 将文本分割成句子
	case Chinese:
		// 中文分割：首先进行分词，然后进行句子边界检测。
		// gse用于高效的中文分词 [3, 4, 5]
		seg, _ := gse.New()
		// 注意：gse的LoadDict()方法可能需要一个字典文件路径。
		// 为了模块的自包含性，理想情况下应使用go:embed嵌入字典。
		// 此处假设gse.New()可以默认使用一个基本内部字典，或用户已处理字典加载。
		// 如果gse无法在没有显式字典的情况下工作，此处需要进一步处理。

		// 使用HMM模式进行分词，通常对新词发现更有效 [3]
		words := seg.Cut(text, true)

		// 中文句子边界检测：
		// 中文句子之间没有空格。我们需要根据标点符号识别句子结尾。
		// 这是一个基于规则的方法。 [13]
		var currentSentenceBuilder strings.Builder
		for _, word := range words {
			currentSentenceBuilder.WriteString(word)
			// 检查常见的中文句末标点符号
			if strings.ContainsAny(word, "。！？") {
				sentences = append(sentences, currentSentenceBuilder.String())
				currentSentenceBuilder.Reset()
			}
			// 注意：像“，”(逗号)或“；”(分号)等其他标点符号不表示句子结束。
			// 中文句子内部词语之间不添加空格。
		}
		// 如果有剩余文本未以标点符号结尾，则将其作为最后一个句子添加
		if currentSentenceBuilder.Len() > 0 {
			sentences = append(sentences, currentSentenceBuilder.String())
		}

	default:
		return nil, fmt.Errorf("不支持的分割语言: %s", lang)
	}

	// 过滤掉可能由于清理或分割导致产生的空句子
	filteredSentences := make([]string, 0, len(sentences))
	for _, s := range sentences {
		trimmed := strings.TrimSpace(s)
		if trimmed != "" {
			filteredSentences = append(filteredSentences, trimmed)
		}
	}
	return strings.Join(filteredSentences, ""), nil
}

// recursiveSummarize 处理超长文档的摘要。
// 它将文本分块，并发地摘要每个分块，然后递归地细化这些摘要。
// 这有助于管理大输入时的内存和性能。 [14]
func recursiveSummarize(sentences string, lang Language, chunkSize int, maxRecursion int) (string, error) {
	if len(sentences) == 0 {
		return "", nil
	}

	// 计算总长度（以rune为单位），以正确处理多字节字符。
	totalLength := 0
	for _, s := range sentences {
		totalLength += utf8.RuneCountInString(s)
	}

	// 递归的基本情况：如果文本足够短或已达到最大递归深度。
	if totalLength <= chunkSize || maxRecursion <= 0 {
		return summarizeChunk(sentences, lang)
	}

	// 将句子分块为更小的部分 [14]
	var chunks []string
	currentChunk := ""
	currentChunkLength := 0

	for _, s := range sentences {
		sentenceRunes := rune(s)
		sentenceLen := len(sentenceRunes)

		// 如果添加当前句子会超出分块大小，并且当前分块不为空，则开始新的分块。
		if currentChunkLength+sentenceLen > chunkSize && len(currentChunk) > 0 {
			chunks = append(chunks, currentChunk)
			currentChunk = ""
			currentChunkLength = 0
		}
		currentChunk = append(currentChunk, s)
		currentChunkLength += sentenceLen
	}
	// 添加最后一个分块（如果它不为空）
	if len(currentChunk) > 0 {
		chunks = append(chunks, currentChunk)
	}

	// 并发摘要每个分块，利用Goroutines [15, 16, 3]
	var wg sync.WaitGroup
	chunkSummaries := make(chan string, len(chunks))
	errChan := make(chan error, 1) // 缓冲一个错误，第一个错误将被返回

	for _, chunk := range chunks {
		wg.Add(1)
		go func(c string) {
			defer wg.Done()
			summary, err := summarizeChunk(c, lang)
			if err != nil {
				select {
				case errChan <- err: // 尝试发送错误，如果通道已满则不阻塞
				default:
					// 错误已发送或通道已满，忽略后续错误
				}
				return
			}
			chunkSummaries <- summary
		}(chunk)
	}

	wg.Wait()
	close(chunkSummaries)
	close(errChan)

	// 检查并发摘要过程中是否存在任何错误
	if len(errChan) > 0 {
		return nil, fmt.Errorf("摘要分块时出错: %w", <-errChan)
	}

	// 聚合所有分块的摘要
	var aggregatedSummaries string
	for summary := range chunkSummaries {
		aggregatedSummaries = append(aggregatedSummaries, summary...)
	}

	// 如果聚合后的摘要仍然过长，则递归地细化它们
	return recursiveSummarize(aggregatedSummaries, lang, chunkSize, maxRecursion-1)
}

// summarizeChunk 对给定的句子切片应用TextRank算法。
// 它返回一个排名靠前的句子切片。
func summarizeChunk(sentences string, lang Language) (string, error) {
	if len(sentences) == 0 {
		return string{}, nil
	}

	tr := textrank.New() // 初始化TextRank [1]

	// TextRank的Populate方法期望一个单一的字符串。用空格连接句子。
	// TextRank根据其语言规则内部处理分词和停用词移除。 [1]
	// 对于中文，TextRank的内部分词器能否正确处理gse提供的空格分隔的中文词语至关重要。
	// 如果不能，则需要一个自定义的textrank.Parser，将gse直接集成到TextRank的解析中。
	// 在此实现中，我们假设TextRank的默认解析器可以处理空格分隔的中文词语。
	textToRank := strings.Join(sentences, " ")
	tr.Populate(textToRank, textrank.NewDefaultLanguage(), textrank.NewDefaultRule())

	tr.Ranking(textrank.NewDefaultAlgorithm()) // 运行TextRank算法对句子进行排名 [1]

	// 检索所有排名靠前的句子。我们获取所有句子，然后由generateFinalSummary处理截断。
	rankedSentences := textrank.FindSentencesByRelationWeight(tr, len(sentences)) // [1]

	var result string
	for _, rs := range rankedSentences {
		result = append(result, rs.Text)
	}
	return result, nil
}

// generateFinalSummary 组合排名靠前的句子，并将其截断到指定的目标字符长度，
// 同时正确处理多字节字符。
func generateFinalSummary(sentences string, targetLength int) string {
	if targetLength <= 0 {
		return ""
	}

	var summaryBuilder strings.Builder                   // 使用strings.Builder进行高效的字符串拼接 [17, 18, 19]
	summaryBuilder.Grow(targetLength + len(sentences)*2) // 预分配容量以减少内存重新分配 [20]

	currentLength := 0

	for _, s := range sentences {
		sentenceRunes := rune(s) // 转换为rune切片以进行准确的字符计数
		sentenceLen := len(sentenceRunes)

		// 检查添加整个句子是否在目标长度内
		if currentLength+sentenceLen+1 <= targetLength { // +1是为了句子之间的空格
			summaryBuilder.WriteString(s)
			summaryBuilder.WriteString(" ") // 在句子之间添加空格
			currentLength += sentenceLen + 1
		} else {
			// 如果不能，则截断当前句子以适应剩余长度
			remainingLength := targetLength - currentLength
			if remainingLength > 0 {
				truncatedSentence := string(sentenceRunes[:remainingLength])
				summaryBuilder.WriteString(truncatedSentence)
			}
			break // 达到或超出目标长度
		}
	}

	return strings.TrimSpace(summaryBuilder.String()) // 移除末尾的空格
}
